package org.raftkv.raft.quorum;

import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class JointConfig implements Serializable {

    public final MajorityConfig[] CONFIGS = new MajorityConfig[2];

    public JointConfig(MajorityConfig c1, MajorityConfig c2) {
        this.CONFIGS[0] = c1;
        this.CONFIGS[1] = c2;
    }

    @Override
    public String toString() {
        return "JointConfig{" +
                "CONFIGS=" + Arrays.toString(CONFIGS) +
                '}';
    }

    /**
     * 该函数返回一个新初始化的集合，表示联合配置中存在的一组投票者。
     */
    public Set<Long> ids() {
        HashSet<Long> m = new HashSet<>();

        for (MajorityConfig cc : CONFIGS) {
            if (cc == null) {
                continue;
            }
            m.addAll(cc);
        }

        return m;
    }

    /**
     * 该函数返回给定查找器的提交索引的（多行）表示形式。
     */
    public String describe(AckedIndexer l) {
        return new MajorityConfig(ids()).describe(l);
    }

    /**
     * 该函数返回给定联合仲裁的最大已提交索引。如果一个 index 在两个组成多数中承诺，则该 index 是共同承诺的。
     */
    public long committedIndex(AckedIndexer l) {
        if (CONFIGS[0] == null) {
            return CONFIGS[1].committedIndex(l);
        } else if (CONFIGS[1] == null) {
            return CONFIGS[0].committedIndex(l);
        } else {
            long idx0 = CONFIGS[0].committedIndex(l);
            long idx1 = CONFIGS[1].committedIndex(l);

            return Math.min(idx0, idx1);
        }
    }

    /**
     * 该函数将选民-投票映射到一个 Map，并返回一个结果，指示投票是 Pending、Lost 还是 Won。<br/>
     * joint quorum 要求两个 majority quorum 都投 Won 才算是 Won。
     */
    public VoteResult voteResult(Map<Long, Boolean> votes) {
        VoteResult r1 = CONFIGS[0] == null ? VoteResult.VoteWon : CONFIGS[0].voteResult(votes);
        VoteResult r2 = CONFIGS[1] == null ? VoteResult.VoteWon : CONFIGS[1].voteResult(votes);

        if (r1 == r2) {
            return r1;
        }

        if (r1 == VoteResult.VoteLost || r2 == VoteResult.VoteLost) {
            return VoteResult.VoteLost;
        }

        return VoteResult.VotePending;
    }

}
